package com.after.saas.security.aspect;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.after.saas.common.annotation.RequireAuthorize;
import com.after.saas.common.exception.AuthorizeException;
import com.after.saas.common.utils.UserUtil;
import com.after.saas.common.utils.JwtUtil;
import com.after.saas.common.utils.RequestUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;

import java.lang.reflect.Method;
import java.util.List;

/**
 * @Desc: PreAuthorizeAspect 自定义权限实现
 * @Author: hjchen
 * @Date: 2021/5/8 16:24
 */

@Aspect
@Component
public class AuthorizeAspect {

    private Logger log = LoggerFactory.getLogger(AuthorizeAspect.class);

    @Value("${jwt.encode.secret}")
    private String secret;

    @Autowired
    private UserUtil tokenService;

    /**
     * 所有权限标识
     */
    private static final String ALL_PERMISSION = "*:*:*";

    /**
     * 管理员角色权限标识
     */
    private static final String SUPER_ADMIN = "admin";


    @Around("@annotation(com.after.saas.common.annotation.RequireAuthorize)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        String structure = String.format("%s.%s", methodSignature.getDeclaringType().getSimpleName(), method.getName());
        RequireAuthorize annotation = method.getAnnotation(RequireAuthorize.class);
        if (annotation == null) {
            return point.proceed();
        }
        if (StrUtil.isNotBlank(annotation.hasPerm())) {
            if (hasPermission(annotation.hasPerm())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        } else if (StrUtil.isNotBlank(annotation.lacksPerm())) {
            if (lacksPermission(annotation.lacksPerm())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        } else if (ObjectUtil.isNotEmpty(annotation.hasAnyPerm())) {
            if (hasAnyPermissions(annotation.hasAnyPerm())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        } else if (StrUtil.isNotBlank(annotation.hasRole())) {
            if (hasRole(annotation.hasRole())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        } else if (!StrUtil.isEmpty(annotation.lacksRole())) {
            if (lacksRole(annotation.lacksRole())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        } else if (ObjectUtil.isNotEmpty(annotation.hasAnyRoles())) {
            if (hasAnyRoles(annotation.hasAnyRoles())) {
                return point.proceed();
            }
            throw new AuthorizeException();
        }
        return point.proceed();
    }

    /**
     * 验证用户是否具备某权限
     *
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermission(String permission) {
        log.warn("权限校验 {}", permission);
        String token = RequestUtil.getToken();
        String userid = JwtUtil.getUserIdFromToken(token, secret);
        List<String> authorities = tokenService.getUserPermissions(userid);
        if (CollectionUtils.isEmpty(authorities)) {
            return false;
        }
        return authorities.stream().filter(StrUtil::isNotBlank)
                .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));

    }

    /**
     * 验证用户是否不具备某权限，与 hasPermission逻辑相反
     *
     * @param permission 权限字符串
     * @return 用户是否不具备某权限
     */
    public boolean lacksPermission(String permission) {
        return !hasPermission(permission);
    }

    /**
     * 验证用户是否具有以下任意一个权限
     *
     * @param permissions 权限列表
     * @return 用户是否具有以下任意一个权限
     */
    public boolean hasAnyPermissions(String... permissions) {
        if (ObjectUtil.isNotEmpty(permissions)) {
            for (String permission : permissions) {
                if (hasPermission(permission)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 验证用户是否不具备某角色，与 isRole逻辑相反。
     *
     * @param role 角色名称
     * @return 用户是否不具备某角色
     */
    public boolean lacksRole(String role) {
        return !hasRole(role);
    }

    /**
     * 验证用户是否具有以下任意一个角色
     *
     * @param roles 角色列表
     * @return 用户是否具有以下任意一个角色
     */
    public boolean hasAnyRoles(String... roles) {
        if (ObjectUtil.isNotEmpty(roles)) {
            for (String role : roles) {
                if (hasRole(role)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断用户是否拥有某个角色
     *
     * @param role 角色字符串
     * @return 用户是否具备某角色
     */
    public boolean hasRole(String role) {
        String token = RequestUtil.getToken();
        String userid = JwtUtil.getUserIdFromToken(token, secret);
        List<String> cacheRoles = tokenService.getUserRoles(userid);
        if (!CollectionUtils.isEmpty(cacheRoles)) {
            for (String roleKey : cacheRoles) {
                if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(role)) {
                    return true;
                }
            }
        }
        return false;
    }

}